Completed
Push — master ( 589f5d...3db258 )
by Sander
14s
created

background.js ➔ getSettings   A

Complexity

Conditions 1
Paths 12

Size

Total Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
c 1
b 1
f 0
nc 12
nop 0
dl 0
loc 48
rs 9.125
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
    API.runtime.onConnect.addListener(function (port) {
10
11
        port.onMessage.addListener(function (msg) {
12
            if (msg === 'credential_amount') {
13
                port.postMessage('credential_amount:' + local_credentials.length);
14
            }
15
16
        });
17
18
    });
19
20
    var master_password = null;
21
22
    function getMasterPasswordSet() {
23
        return (master_password !== null);
24
    }
25
26
    _self.getMasterPasswordSet = getMasterPasswordSet;
27
28
    function setMasterPassword(opts) {
29
        master_password = opts.password;
30
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
31
            // Save the password in plain text on user request.
32
            // No secure local storage is available :/
33
            storage.set('master_password', opts.password);
34
        } else {
35
            storage.set('master_password', null);
36
        }
37
38
        if (opts.password) {
39
            getSettings();
40
        } else {
41
            displayLogoutIcons();
42
        }
43
44
    }
45
46
    _self.setMasterPassword = setMasterPassword;
47
48
49
    var testMasterPasswordAgainst;
50
51
    function isMasterPasswordValid(password) {
52
        try {
53
            PAPI.decryptString(testMasterPasswordAgainst, password);
54
            return true;
55
        } catch (e) {
56
            return false;
57
        }
58
    }
59
60
    _self.isMasterPasswordValid = isMasterPasswordValid;
61
62
63
    var local_credentials = [];
64
    var local_vault = [];
65
    var encryptedFieldSettings = ['accounts'];
66
    _self.settings = {};
67
    _self.ticker = null;
68
    _self.running = false;
69
    function getSettings() {
70
71
        storage.get('settings').then(function (_settings) {
72
            if ((!_settings || Object.keys(_settings).length === 0 || !_settings.hasOwnProperty('accounts')) && !master_password) {
73
                return;
74
            }
75
76
            if (!master_password && _settings.hasOwnProperty('accounts') && _settings.accounts.length > 0) {
77
                _self.settings.isInstalled = 1;
78
                testMasterPasswordAgainst = _settings.accounts;
79
                return;
80
            }
81
82
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
83
                var field = encryptedFieldSettings[i];
84
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
85
            }
86
87
            _self.settings = _settings;
88
89
            if (!_self.settings.hasOwnProperty('ignored_sites')) {
90
                _self.settings.ignored_sites = [];
91
            }
92
93
            if (!_self.settings.hasOwnProperty('no_results_found_tab')) {
94
                _self.settings.no_results_found_tab = 'list';
95
            }
96
97
            if (!_self.settings.hasOwnProperty('enablePasswordPicker')) {
98
                _self.settings.enablePasswordPicker = !_self.settings.disablePasswordPicker ;
99
            }
100
            
101
            if (!_self.settings.hasOwnProperty('enableAutoFill')) {
102
                _self.settings.enableAutoFill = !_self.settings.disableAutoFill;
103
            }
104
105
            getCredentials();
106
107
            if (_self.running) {
108
                clearInterval(_self.ticker);
109
            }
110
            _self.running = true;
111
            _self.ticker = setInterval(function () {
112
113
            }, _self.settings.refreshTime * 1000);
114
115
        });
116
    }
117
118
    _self.getSettings = getSettings;
119
120
    function getRuntimeSettings() {
121
        return _self.settings;
122
    }
123
124
    _self.getRuntimeSettings = getRuntimeSettings;
125
126
    function getSetting(name) {
127
        return _self.settings[name];
128
    }
129
130
    _self.getSetting = getSetting;
131
132
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
133
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
134
            var field = encryptedFieldSettings[i];
135
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
136
        }
137
138
        if (!settings.hasOwnProperty('ignored_sites')) {
139
            settings.ignored_sites = [];
140
        }
141
142
        if (!_self.settings.hasOwnProperty('password_picker_first_tab')) {
143
            _self.settings.disable_browser_autofill = 'list';
144
        }
145
146
        //window.settings contains the run-time settings
147
        _self.settings = settings;
148
149
150
        storage.set('settings', settings).then(function () {
151
            getSettings();
152
        });
153
154
    }
155
156
    _self.saveSettings = saveSettings;
157
158
159
    function getCredentials() {
160
        if (!master_password) {
161
            return;
162
        }
163
        //console.log('Loading vault with the following settings: ', settings);
164
        var tmpList = [];
165
166
        for (var i = 0; i < _self.settings.accounts.length; i++) {
167
            var account = _self.settings.accounts[i];
168
            /* jshint ignore:start */
169
            (function (inner_account) {
170
                PAPI.getVault(inner_account, function (vault) {
171
                    if (vault.hasOwnProperty('error')) {
172
                        return;
173
                    }
174
                    var _credentials = vault.credentials;
175
                    for (var i = 0; i < _credentials.length; i++) {
176
                        var key = inner_account.vault_password;
177
                        var credential = _credentials[i];
178
                        if (credential.hidden === 1) {
179
                            continue;
180
                        }
181
                        var usedKey = key;
182
                        //Shared credentials are not implemented yet
183
                        if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
184
                            usedKey = PAPI.decryptString(credential.shared_key, key);
185
186
                        }
187
                        credential = PAPI.decryptCredential(credential, usedKey);
188
                        credential.account = inner_account;
189
                        if (credential.delete_time === 0) {
190
                            tmpList.push(credential);
191
                        }
192
193
                    }
194
                    delete vault.credentials;
195
                    local_vault = vault;
196
                    local_credentials = tmpList;
197
198
                    getSharedCredentials(inner_account);
199
200
201
                });
202
            }(account));
203
            /* jshint ignore:end */
204
        }
205
    }
206
207
    _self.getCredentials = getCredentials;
208
209
    function getSharedCredentials(account) {
210
        PAPI.getCredendialsSharedWithUs(account, account.vault.guid, function (credentials) {
211
            for (var i = 0; i < credentials.length; i++) {
212
                var _shared_credential = credentials[i];
213
                var _shared_credential_data;
214
                var sharedKey = PAPI.decryptString(_shared_credential.shared_key, account.vault_password);
215
                try {
216
                    _shared_credential_data = PAPI.decryptSharedCredential(_shared_credential.credential_data, sharedKey);
217
                } catch (e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
218
219
                }
220
                if (_shared_credential_data) {
221
                    delete _shared_credential.credential_data;
222
                    _shared_credential_data.acl = _shared_credential;
223
                    _shared_credential_data.acl.permissions = new SharingACL(_shared_credential_data.acl.permissions);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
224
                    _shared_credential_data.tags_raw = _shared_credential_data.tags;
225
                    _shared_credential_data.account = account;
226
                    local_credentials.push(_shared_credential_data);
227
                }
228
            }
229
            updateTabsIcon();
230
        });
231
    }
232
233
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
234
        if (!master_password) {
235
            return [];
236
        }
237
        if (!_url || _url === '') {
238
            return [];
239
        }
240
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
241
        var found_list = [];
242
        for (var i = 0; i < local_credentials.length; i++) {
243
            var credential_url = local_credentials[i].url;
244
            if (!/^(ht)tps?:\/\//i.test(credential_url) && credential_url !== '' && _url) {
245
                try {
246
                    var protocol = _url.split('://').shift();
247
                    credential_url = protocol + "://" + credential_url;
248
                } catch (e){
249
                    //ignore
250
                }
251
            }
252
            credential_url = processURL(credential_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
253
            if (credential_url) {
254
                if (credential_url.indexOf(url) !== -1) {
255
                    found_list.push(local_credentials[i]);
256
                }
257
            }
258
259
        }
260
        return found_list;
261
    }
262
263
    _self.getCredentialsByUrl = getCredentialsByUrl;
264
265
266
    function saveCredential(credential) {
267
        //@TODO save shared password
268
        if (!credential.credential_id) {
269
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
270
                local_credentials.push(createdCredential);
271
            });
272
        } else {
273
            var credential_index;
274
            for (var i = 0; i < local_credentials.length; i++) {
275
                if (local_credentials[i].guid === credential.guid) {
276
                    credential_index = i;
277
                    break;
278
                }
279
            }
280
281
            if (credential.hasOwnProperty('acl')) {
282
                var permissons = new SharingACL(credential.acl.permissions.permission);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
283
                if (!permissons.hasPermission(0x02)) {
284
                    return;
285
                }
286
            }
287
288
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
289
                if (credential_index) {
290
                    local_credentials[credential_index] = updatedCredential;
291
                }
292
            });
293
        }
294
    }
295
296
    _self.saveCredential = saveCredential;
297
298
    function getCredentialByGuid(guid) {
299
        for (var i = 0; i < local_credentials.length; i++) {
300
            var credential = local_credentials[i];
301
            if (credential.guid === guid) {
302
                return credential;
303
            }
304
        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
305
    }
306
307
    _self.getCredentialByGuid = getCredentialByGuid;
308
309
    function getCredentialForHTTPAuth(req) {
310
        return getCredentialsByUrl(req.url)[0];
311
    }
312
313
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
314
315
    var mined_data = [];
316
317
    function minedForm(data, sender) {
318
        var url = sender.url;
319
        var existingLogins = getCredentialsByUrl(sender.url);
320
        var title = API.i18n.getMessage('detected_new_login') + ':';
321
        var minedMatchingID = null;
322
        for (var j = 0; j < existingLogins.length; j++) {
323
            var login = existingLogins[j];
324
            if (login.username === data.username) {
325
                if (login.password !== data.password) {
326
                    minedMatchingID = login.guid;
327
                    title = API.i18n.getMessage('detected_changed_login') + ':';
328
                }
329
                else {
330
                    //console.log('No changes detected');
331
                    delete mined_data[sender.tab.id];
332
                    return;
333
                }
334
            }
335
        }
336
        mined_data[sender.tab.id] = {
337
            title: title,
338
            url: url,
339
            username: data.username,
340
            password: data.password,
341
            label: sender.title,
342
            guid: minedMatchingID
343
        };
344
345
        //console.log('Done mining, ', mined_data, sender.tab.id);
346
    }
347
348
    _self.minedForm = minedForm;
349
350
    function getMinedData(args, sender) {
351
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
352
        var senderUrl = sender.tab.url;
353
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
354
        if (!_self.settings) {
355
            return null;
356
        }
357
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
358
            return mined_data[sender.tab.id];
359
        }
360
        var matches = _self.settings.ignored_sites.filter(function (item) {
361
            return typeof item === 'string' && site.indexOf(item) > -1;
362
        });
363
364
        if (matches.length !== 0) {
365
            return null;
366
        }
367
        return mined_data[sender.tab.id];
368
    }
369
370
    _self.getMinedData = getMinedData;
371
372
    function clearMined(args, sender) {
373
        delete mined_data[sender.tab.id];
374
    }
375
376
    _self.clearMined = clearMined;
377
378
    function saveMinedCallback(args) {
379
        createIconForTab(args.sender.tab);
380
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
381
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
382
            });
383
        });
384
    }
385
386
    function ignoreSite(_url) {
387
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
388
            _self.settings.ignored_sites = [];
389
        }
390
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
391
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
392
            _self.settings.ignored_sites.push(site);
393
            saveSettings(_self.settings);
394
        }
395
    }
396
397
    _self.ignoreSite = ignoreSite;
398
399
    function passToParent(args, sender) {
400
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
401
        });
402
    }
403
404
    _self.passToParent = passToParent;
405
406
    function getActiveTab(opt) {
407
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
408
            var tab = tabs[0];
409
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
410
            });
411
        });
412
    }
413
414
    _self.getActiveTab = getActiveTab;
415
416
    function updateCredentialUrlDoorhanger(login) {
417
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
418
            var tab = tabs[0];
419
            var data = login;
420
            data.url = tab.url;
421
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
422
            API.tabs.sendMessage(tab.id, {
423
                method: 'showUrlUpdateDoorhanger',
424
                args: {data: data}
425
            });
426
        });
427
    }
428
429
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
430
431
    function updateCredentialUrl(data, sender) {
432
        mined_data[sender.tab.id] = data;
433
        saveMined({}, sender);
434
435
    }
436
437
    _self.updateCredentialUrl = updateCredentialUrl;
438
439
    function saveMined(args, sender) {
440
        var data = mined_data[sender.tab.id];
441
        var credential = {},
442
            credential_index;
443
444
        if (data.guid === null) {
445
            credential = PAPI.newCredential();
446
        } else {
447
            for (var i = 0; i < local_credentials.length; i++) {
448
                if (local_credentials[i].guid === data.guid) {
449
                    credential = local_credentials[i];
450
                    credential_index = i;
451
                    break;
452
                }
453
            }
454
        }
455
        if (!credential.hasOwnProperty('account')) {
456
            credential.account = args.account;
457
        }
458
        credential.username = data.username;
459
        credential.password = data.password;
460
        credential.url = sender.tab.url;
461
        if (credential.guid !== null) {
462
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
463
                updatedCredential.account = credential.account;
464
                if (credential_index) {
465
                    local_credentials[credential_index] = updatedCredential;
466
                }
467
                saveMinedCallback({credential: credential, updated: true, sender: sender});
468
                delete mined_data[sender.tab.id];
469
            });
470
        } else {
471
            credential.label = sender.tab.title;
472
            credential.vault_id =  credential.account.vault.vault_id;
473
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
474
                createdCredential.account = credential.account;
475
                saveMinedCallback({credential: credential, updated: false, sender: sender});
476
                local_credentials.push(createdCredential);
477
                delete mined_data[sender.tab.id];
478
            });
479
        }
480
    }
481
482
    _self.saveMined = saveMined;
483
484
    function searchCredential(searchText) {
485
        searchText = searchText.toLowerCase();
486
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
487
        var results = [];
488
        for (var i = 0; i < local_credentials.length; i++) {
489
            var credential = local_credentials[i];
490
            for (var f = 0; f < searchFields.length; f++) {
491
                var field = searchFields[f];
492
                if (!credential[field]) {
493
                    continue;
494
                }
495
496
                var field_value = credential[field].toLowerCase();
497
                if (field_value.indexOf(searchText) !== -1) {
498
                    results.push(credential);
499
                    break;
500
                }
501
            }
502
        }
503
        return results;
504
    }
505
506
    _self.searchCredential = searchCredential;
507
508
509
    function injectCreateCredential(args, sender) {
510
        var account = getRuntimeSettings().accounts[args.vaultIndex];
511
        var credential = PAPI.newCredential();
512
        credential.label = args.label;
513
        credential.username = args.username;
514
        credential.password = args.password;
515
        credential.vault_id = local_vault.vault_id;
516
        credential.url = sender.tab.url;
517
        PAPI.createCredential(account, credential, account.vault_password, function (createdCredential) {
518
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
519
            local_credentials.push(createdCredential);
520
521
        });
522
    }
523
524
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
525
526
    function isVaultKeySet() {
527
        return (_self.settings.vault_password !== null);
528
    }
529
530
    _self.isVaultKeySet = isVaultKeySet;
531
532
    function isAutoFillEnabled() {
533
        if (!_self.settings.hasOwnProperty('enableAutoFill')) {
534
            return true;
535
        }
536
        return _self.settings.enableAutoFill;
537
    }
538
539
    _self.isAutoFillEnabled = isAutoFillEnabled;
540
541
    var doorhangerData = null;
542
543
    function setDoorhangerData(data) {
544
        doorhangerData = data;
545
    }
546
547
    _self.setDoorhangerData = setDoorhangerData;
548
549
    function getDoorhangerData() {
550
        return doorhangerData;
551
    }
552
553
    _self.getDoorhangerData = getDoorhangerData;
554
555
    function closeSetupTab() {
556
        API.tabs.query({url: 'chrome-extension://' + API.runtime.id + '/html/browser_action/browser_action.html'}).then(function (tabs) {
557
            if (tabs) {
558
                API.tabs.remove(tabs[0].id);
559
            }
560
        });
561
    }
562
563
    _self.closeSetupTab = closeSetupTab;
564
565
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
566
567
        if (!msg || !msg.hasOwnProperty('method')) {
568
            return;
569
        }
570
        var result = false;
571
        if (_self[msg.method]) {
572
            result = _self[msg.method](msg.args, sender);
573
        } else {
574
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
575
        }
576
577
        sendResponse(result);
578
    });
579
580
    var defaultColor = '#0082c9';
581
582
    function createIconForTab(tab) {
583
        if (!master_password) {
584
            return;
585
        }
586
        var tabUrl = tab.url;
587
        var logins = getCredentialsByUrl(tabUrl);
588
        if (tab.active) {
589
            window.contextMenu.setContextItems(logins);
590
        }
591
        var credentialAmount = logins.length;
592
        API.browserAction.setBadgeText({
593
            text: credentialAmount.toString(),
594
            tabId: tab.id
595
        });
596
        API.browserAction.setBadgeBackgroundColor({
597
            color: defaultColor,
598
            tabId: tab.id
599
        });
600
601
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
602
        API.browserAction.setTitle({
603
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
604
            tabId: tab.id
605
        });
606
    }
607
608
    function displayLogoutIcons() {
609
        if (_self.settings) {
610
            API.tabs.query({}).then(function (tabs) {
611
                for (var t = 0; t < tabs.length; t++) {
612
                    var tab = tabs[t];
613
                    API.browserAction.setBadgeText({
614
                        text: '🔑',
615
                        tabId: tab.id
616
                    });
617
                    API.browserAction.setBadgeBackgroundColor({
618
                        color: '#ff0000',
619
                        tabId: tab.id
620
                    });
621
                    API.browserAction.setTitle({
622
                        title: API.i18n.getMessage('browser_action_title_locked'),
623
                        tabId: tab.id
624
                    });
625
                }
626
            });
627
        }
628
    }
629
630
    function updateTabsIcon() {
631
        API.tabs.query({}).then(function (tabs) {
632
            for (var t = 0; t < tabs.length; t++) {
633
                var tab = tabs[t];
634
                createIconForTab(tab);
635
            }
636
        });
637
    }
638
639
640
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
641
        if (master_password) {
642
            createIconForTab(tab);
643
        } else {
644
            displayLogoutIcons();
645
        }
646
    });
647
648
    API.tabs.onActivated.addListener(function () {
649
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
650
            if (master_password) {
651
                createIconForTab(tabs[0]);
652
            } else {
653
                displayLogoutIcons();
654
            }
655
        });
656
    });
657
658
    displayLogoutIcons();
659
660
661
    storage.get('master_password').then(function (password) {
662
        if (password) {
663
            master_password = password;
664
            API.api.browserAction.setBadgeBackgroundColor({
665
                color: defaultColor
666
            });
667
        }
668
        getSettings();
669
    }).error(function (error) {
670
        if (error === "Data not found") {
671
            getSettings();
672
        }
673
    });
674
    return _window;
675
}());
676
677